﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace WPHttpServer
{
    /// <summary>
    /// Handles an incomming connection
    /// </summary>
    /// <param name="incommingData">The incomming request as byte buffer</param>
    /// <returns>The response bytes</returns>
    public delegate Task<IEnumerable<byte>> ConnectionReceivedHandler(byte[] incommingData);

    internal class ConnectionManager
    {
        #region ConnectionReceived-Event
        public event ConnectionReceivedHandler ConnectionReceived;

        private Task<IEnumerable<byte>> OnConnectionReceived(byte[] incommingdata)
        {
            ConnectionReceivedHandler handler = this.ConnectionReceived;
            if (handler != null)
            {
                return handler(incommingdata);
            }
            else
            {
                return null;
            }
        } 
        #endregion

        private StreamSocketListener _socketListener;

        private const uint DEFAULT_BUFFER_SIZE = 1024 * 1024 * 10; // 10 kb

        public string ServiceName { get; private set; }
        public uint MaxRequestSize { get; set; }

        public ConnectionManager(string serviceName, uint maxRequestSize = DEFAULT_BUFFER_SIZE)
        {
            ServiceName = serviceName;
            MaxRequestSize = maxRequestSize;
        }

        public void StartListening()
        {
            Task.Run(async () =>
            {
                this._socketListener = new StreamSocketListener();

                this._socketListener.ConnectionReceived += SocketListenerOnConnectionReceived;
                await this._socketListener.BindServiceNameAsync(ServiceName);
            });
        }

        private async void SocketListenerOnConnectionReceived(StreamSocketListener sender, 
                                                              StreamSocketListenerConnectionReceivedEventArgs args)
        {
            try
            {
                using (args.Socket)
                {
                    byte[] requestBuffer;
                    using (DataReader reader = new DataReader(args.Socket.InputStream))
                    {
                        reader.InputStreamOptions = InputStreamOptions.Partial;

                        await reader.LoadAsync(MaxRequestSize);
                        requestBuffer = new byte[reader.UnconsumedBufferLength];
                        reader.ReadBytes(requestBuffer);
                    }

                    IEnumerable<byte> responseData = await this.OnConnectionReceived(requestBuffer);

                    using (DataWriter writer = new DataWriter(args.Socket.OutputStream))
                    {
                        writer.WriteBytes(responseData.ToArray());

                        await writer.StoreAsync();
                    }
                }
            }
            finally 
            {
                //GC.Collect(0, GCCollectionMode.Forced, true);
                //GC.Collect(1, GCCollectionMode.Forced, true);
                //GC.Collect(2, GCCollectionMode.Forced, true);
            }
        }
    }
}